#![cfg_attr(
  all(not(debug_assertions), target_os = "windows"),
  windows_subsystem = "windows"
)]

use std::{
  env,
  error::Error,
  fs::{canonicalize, read},
};

use serde::{Deserialize, Serialize};
use std::{iter::once, os::windows::prelude::OsStrExt};
use tauri::{
  api,
  http::{Request, Response, ResponseBuilder},
  AppHandle, CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu,
};
use webview2_com::Microsoft::Web::WebView2::Win32::*;
use windows::core::{Interface, PCWSTR};

use window_vibrancy;

// use cmd;
mod cmd;
mod ssh;

#[derive(Serialize, Deserialize)]
struct HttpPost {
  foo: String,
  bar: String,
}

#[derive(Serialize)]
struct HttpReply {
  msg: String,
  request: HttpPost,
}

fn handle_system_tray_event(app: &AppHandle, event: SystemTrayEvent) {
  match event {
    SystemTrayEvent::LeftClick {
      position: _,
      size: _,
      ..
    } => {
      let window = app.get_window("main").unwrap();
      if window.is_visible().unwrap() {
        window.hide().unwrap();
      } else {
        window.show().unwrap();
        window.set_focus().unwrap();
      }
    }
    SystemTrayEvent::MenuItemClick { id, .. } => {
      let item_handle = app.tray_handle().get_item(&id);
      match id.as_str() {
        "toggle" => {
          let window = app.get_window("main").unwrap();
          let new_title = if window.is_visible().unwrap() {
            window.hide().unwrap();
            "打开"
          } else {
            window.show().unwrap();
            "隐藏"
          };
          item_handle.set_title(new_title).unwrap();
        }
        "退出" => {
          let window = app.get_window("main").unwrap();
          window.close().unwrap();
        }
        _ => {}
      }
    }
    _ => {}
  }
}

fn create_system_tray() -> SystemTray {
  SystemTray::new().with_menu(SystemTrayMenu::new().add_item(CustomMenuItem::new("exit", "退出")))
}

fn handle_register_uri_scheme_protocol(
  app: &AppHandle,
  request: &Request,
) -> Result<Response, Box<dyn Error>> {
  let path = request.uri().replace("plugin://localhost/", "");
  println!("uri: {}", request.uri());

  let base = api::path::resolve_path(
    &app.config(),
    app.package_info(),
    &app.env(),
    "extensions",
    Some(api::path::BaseDirectory::Resource),
  )
  .unwrap();
  let path = base.join(&path);
  println!("{:?}", path);
  let content = read(canonicalize(&path)?)?;
  let (data, meta) = if path.extension().unwrap() == "html" {
    (content, "text/html")
  } else if path.ends_with(".js") {
    (content, "text/javascript")
  } else if path.ends_with(".png") {
    (content, "image/png")
  } else {
    (content, "text/plain")
  };

  ResponseBuilder::new()
    .header("Access-Control-Allow-Origin", "*")
    .mimetype(meta)
    .body(data)
}

fn encode_wide(string: impl AsRef<std::ffi::OsStr>) -> Vec<u16> {
  string.as_ref().encode_wide().chain(once(0)).collect()
}

fn main() {
  tauri::Builder::default()
    .setup(|app| {
      let main_window = app.get_window("main").unwrap();

      #[cfg(target_os = "windows")]
      window_vibrancy::apply_blur(&main_window, Some((18, 18, 18, 125)))
        .expect("window_vibrancy error");

      let extensions_dir = app
        .path_resolver()
        .resolve_resource("extensions")
        .expect("failed to resolve resource dir");

      let dist_path = app
        .path_resolver()
        .resolve_resource("../dist")
        .unwrap()
        .to_string_lossy()
        .to_string();

      println!("{}", dist_path);

      main_window
        .with_webview(move |webview| {
          #[cfg(windows)]
          unsafe {
            let controller = webview.controller();
            let webview2 = controller.CoreWebView2().unwrap();
            let webview3 = webview2.cast::<ICoreWebView2_3>().unwrap();

            let _ = webview3
              .SetVirtualHostNameToFolderMapping(
                PCWSTR::from_raw(encode_wide("lib.localhost".to_string()).as_ptr()),
                PCWSTR::from_raw(
                  encode_wide(dist_path.trim_start_matches(r"\\?\").replace("\\", "/")).as_ptr(),
                ),
                COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW,
              )
              .unwrap();

            for entry in extensions_dir.read_dir().expect("read_dir call failed") {
              if let Ok(entry) = entry {
                if entry.path().is_file() {
                  continue;
                }

                let name = entry
                  .path()
                  .file_name()
                  .unwrap()
                  .to_string_lossy()
                  .to_string();

                let hostname =
                  PCWSTR::from_raw(encode_wide(format!("{}.extension", name)).as_ptr());
                let folderpath = PCWSTR::from_raw(
                  encode_wide(
                    entry
                      .path()
                      .to_str()
                      .unwrap()
                      .trim_start_matches(r"\\?\")
                      .replace("\\", "/"),
                  )
                  .as_ptr(),
                );

                let _ = webview3
                  .SetVirtualHostNameToFolderMapping(
                    hostname,
                    folderpath,
                    COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW,
                  )
                  .map_err(|err| println!("{:?}", err));
              }
            }
          }
        })
        .unwrap();
      Ok(())
    })
    .system_tray(create_system_tray())
    .on_system_tray_event(handle_system_tray_event)
    .register_uri_scheme_protocol("plugin", handle_register_uri_scheme_protocol)
    .manage(ssh::SshMap(Default::default()))
    .invoke_handler(tauri::generate_handler![
      cmd::getenv,
      cmd::exists,
      cmd::get_manifest,
      cmd::open_devtools,
      cmd::get_all_extensions,
      ssh::connect,
      ssh::run,
    ])
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
}
